<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>配参变更历史记录及恢复管理</title>
        <script src="./lib/axios.js"></script>
        <script src="./lib/vue.js"></script>
        <link rel="stylesheet" href="./lib/elementindex.css">
        <script src="./lib/elementindex.js"></script>
        <style>
            .el-dialog__header{
                    padding-top: 10px;
                    padding-bottom: 5px;
                }
            .el-dialog__body{
                padding-top: 0px;
            }
        </style>
    </head>
    <body>
        <div id='app'>
            <el-card style="font-size:small;margin-bottom: 1.125rem;">
                <el-link :href="'./home.html?profile='+profile" style="color: blue;">&lt;&lt;&lt;返回配参查看编辑页面</el-link>
            </el-card>
            <el-form>
                <el-table v-loading="listLoading" border :data="configList" style="width: 100%;margin-bottom: 20px;"
                    row-key="id">
                    <el-table-column label="#" prop="changed_id" width='60px'>
                    </el-table-column>

                    <el-table-column label="变更汇总" prop="changed_summary" width='360px'>
                        <template slot-scope="scope">
                            &nbsp;更新环境<el-tag v-for="prof in scope.row.changed_summary.profiles" size="mini" type='success'>{{prof}}</el-tag>
                            配参,共<span style="font-weight:bold;color: red;">{{scope.row.changed_summary.count}}</span>条.

                            <el-table size="mini" v-if="scope.row.changed_summary.remarks" :show-header="false" :data="scope.row.changed_summary.remarks"
                                style="width: 100%;margin: 0px;">
                                <el-table-column prop="time" label="发布时间" :show-overflow-tooltip="true">
                                    <template slot-scope="scope">
                                        {{scope.row.remark}}
                                        <el-tag size="mini">{{scope.row.action}}</el-tag> by {{scope.row.user}} from
                                        {{scope.row.ip}} @
                                        <span>{{new Date(scope.row.time ).toLocaleDateString() + ' ' + new Date(scope.row.time).toLocaleTimeString()}}</span>
                                    </template>
                                </el-table-column>
                            </el-table>

                            <el-table size="mini" v-if="scope.row.changed_summary.nodes" :show-header="true" :data="scope.row.changed_summary.nodes"
                                style="width: 100%;margin:0px;">
                                <template slot="empty">
                                    <span>没有发布到任何应用节点。</span>
                                </template>
                                <el-table-column prop="name" label="应用节点" :show-overflow-tooltip="true">
                                </el-table-column>
                                <el-table-column prop="time" label="发布时间" :show-overflow-tooltip="true">
                                    <template slot-scope="scope">
                                        {{scope.row.user}}@<span>{{new Date(scope.row.time ).toLocaleDateString() + ' ' + new Date(scope.row.time).toLocaleTimeString()}}</span>
                                    </template>
                                </el-table-column>
                            </el-table>
                        </template>
                    </el-table-column>

                    <el-table-column label="变更内容" prop="changed_content">
                        <template slot-scope="scope">
                            <el-table :show-header="false" :data="scope.row.changed_content" style="width: 100%;margin-bottom: 20px;">
                                <template slot="empty">
                                    <span style="font-weight: bold; font-size: 14px;">全部变更清单因缺少数据权限而不能查看。</span>
                                </template>
                                <el-table-column :show-overflow-tooltip="true">
                                    <template slot-scope="scope">
                                        {{scope.row.code}}
                                        <el-tag size="mini">{{scope.row.act}}</el-tag>
                                        &nbsp;
                                        <el-tooltip v-if="/^deleted$|^setup$/.test(scope.row.act)" content="Bottom center"
                                            placement="bottom" effect="light">
                                            <i class="el-icon-chat-dot-square"></i>
                                            <div slot="content">
                                                <div>名称：{{scope.row.oname}} <span v-if="scope.row.oname != scope.row.nname">->
                                                        {{scope.row.nname}}</span></div>
                                                <div>结构：{{scope.row.oschema}} <span v-if="JSON.stringify(scope.row.oschema) !== JSON.stringify(scope.row.nschema)">->
                                                        {{scope.row.nschema}}</span></div>
                                            </div>
                                        </el-tooltip>
                                        &nbsp;
                                        <span v-if="scope.row.act!=='created'" style="color:gray;">
                                            {{scope.row.oval}}
                                            <el-tag v-if="scope.row.ostat & 2" size="mini">密</el-tag>
                                            <el-tag v-if="!scope.row.oval" size="mini">空</el-tag>
                                        </span>
                                        &nbsp;
                                        <i class="el-icon-caret-right" style="color: royalblue;"></i>
                                        <i class="el-icon-caret-right" style="color: royalblue;"></i>
                                        <i class="el-icon-caret-right" style="color: royalblue;"></i>
                                        &nbsp;
                                        <span v-if="scope.row.act!=='deleted'" style="color: #000000;font-weight: bold;">
                                            {{scope.row.nval}}
                                            <el-tag v-if="scope.row.nstat & 2" size="mini">密</el-tag>
                                            <el-tag v-if="!scope.row.nval" size="mini">空</el-tag>
                                        </span>
                                    </template>
                                </el-table-column>
                            </el-table>
                        </template>
                    </el-table-column>

                    <el-table-column label="变更触发" prop="changed_time" width='120px'>
                        <template slot-scope="scope">
                            <span>{{scope.row.changed_user}}@</span>
                            <span>{{new Date(scope.row.changed_time).toLocaleDateString() + ' ' + new Date(scope.row.changed_time).toLocaleTimeString()}}</span>
                        </template>
                    </el-table-column>

                    <el-table-column label="变更状态" prop="changed_status" width='80px'>
                        <template slot-scope="scope">
                            <span>{{changedStatusNames[scope.row.changed_status]}}</span>
                        </template>
                    </el-table-column>

                    <el-table-column title="操作" width='120px'>
                        <template slot="header">
                            <span>操作</span>
                        </template>
                        <template slot-scope="scope">
                            <el-button size="mini" v-if="/^stored$|^graying$/.test(changedStatusCodes[scope.row.changed_status]) && context.updater==='rabbitmq'"
                                plain type="primary" @click="triggerGrayPublish(scope.row)">
                                <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                    <i class="el-icon-finished">灰度发布</i>
                                    <div slot="content">
                                        <span>变更已保存但灰度发布未完成，需要继续完成灰度发布。</span>
                                    </div>
                                </el-tooltip>
                            </el-button>

                            <div v-if="changedStatusCodes[scope.row.changed_status] == 'planed'">
                                <el-button size="mini" plain type="primary" @click="handleChangeManage(scope.row,'published','变更发布')">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <i class="el-icon-circle-check">变更发布</i>
                                        <div slot="content">
                                            <span>对此次变更计划(申请)进行发布，立即生效。</span>
                                        </div>
                                    </el-tooltip>
                                </el-button><br />
                                <el-button v-if="context.updater==='rabbitmq'" size="mini" plain type="primary" @click="handleChangeManage(scope.row,'stored','灰度发布')">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <i class="el-icon-finished">灰度发布</i>
                                        <div slot="content">
                                            <span>对此次变更计划(申请)进行灰度发布，逐步在应用节点生效。</span>
                                        </div>
                                    </el-tooltip>
                                </el-button><br v-if="context.updater==='rabbitmq'" />
                                <el-button size="mini" plain @click="handleChangeManage(scope.row,'canceled','变更撤销')">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <i class="el-icon-circle-close">变更撤销</i>
                                        <div slot="content">
                                            <span>对此次变更计划(申请)作废，不再生效或发布。</span>
                                        </div>
                                    </el-tooltip>
                                </el-button>
                            </div>
                            <div v-if="/^published$|^terminated$/.test(changedStatusCodes[scope.row.changed_status])">
                                <el-button plain size='mini' @click="handleRestore(scope.row,'restore-version','整体回滚')">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <i class="el-icon-refresh-left">整体回滚</i>
                                        <div slot="content">
                                            <span>从最新(最后)一次变更开始，
                                                <span style="font-weight: bold;color: red;">所有内容</span>逆向依次恢复到当前变更之前(此次修改被恢复)</span>
                                        </div>
                                    </el-tooltip>
                                </el-button><br />
                                <el-button plain size='mini' @click="handleRestore(scope.row,'restore-change-before','批次回滚')">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <i class="el-icon-refresh-left">批次回滚</i>
                                        <div slot="content">
                                            <span>把此次变更所涉及的配参，恢复到<span style="font-weight: bold;color: red;">此次变更之前</span>的内容(此次结果错误，需修复)</span>
                                        </div>
                                    </el-tooltip>
                                </el-button><br />
                                <el-button plain size='mini' @click="handleRestore(scope.row,'restore-change-after','批次恢复')">
                                    <el-tooltip content="Bottom center" placement="bottom" effect="light">
                                        <i class="el-icon-refresh-left">批次恢复</i>
                                        <div slot="content">
                                            <span>把此次变更所涉及的配参，恢复到<span style="font-weight: bold;color: red;">此次变更之后</span>的内容(此次结果正确，需保留)</span>
                                        </div>
                                    </el-tooltip>
                                </el-button>
                            </div>

                        </template>
                    </el-table-column>
                </el-table>

                <el-pagination layout="total, sizes, prev, pager, next, jumper" :page-sizes="[10,20,50,100,200]"
                    :page-size="pageSize" :current-page="currentPage" :total="totalCount" @size-change="handleSizeChange"
                    @current-change="handleCurrentChange">
                </el-pagination>

            </el-form>
        </div>

        <script type='module'>
            import * as $util from './lib/util.js'
            
            window.hosturl = document && document.location.href.startsWith('http://127.0.0.1:8848') ? 'http://localhost/resources' : '.';
            
            function restore(row,cmd,name)
            {
               let inputPlaceholder =`对${row.changed_user}在${new Date(row.changed_time).toLocaleDateString()}的变更[${row.changed_id}]执行[${name}]操作`;
               vm.$prompt(`此次[${name}]操作确定后会立即执行, 是否继续?`, '提示', {
                          confirmButtonText: '确定',
                          cancelButtonText: '取消',
                          inputPlaceholder:inputPlaceholder,
                        }).then(({ value }) => {
                          console.warn(value);
                          doResore(value?value:inputPlaceholder);
                        }).catch(() => {
                           console.log(`${name} is cancelled`);
                        });
                
                function doResore(remark)
                {
                    axios.post(window.hosturl +`/${cmd}`,{changeId:row.changed_id,remark:remark}).then(
                    resp =>{
                        if(resp.data.error=='OngoingConflictException')
                        {
                            vm.$message({
                                    showClose:true,
                                     type:'error',
                                     message:`当前变更记录与其它进行中变更记录重叠，无法进行。${resp.data.data}`
                                 });
                            return;
                        }else if(typeof resp.data!=='object' || resp.data.error)
                        {
                            vm.$message({
                                    showClose:true,
                                     type:'error',
                                     message:resp.data.data
                                 });
                            return;
                        }
                         else refresh();
                    }).catch(ex=>{vm.$message({showClose:true,type:"error",message:ex});});
                }
            }
            
            function refresh()
            {
                vm.listLoading = true;
                axios.post(window.hosturl +`/change-history`,{pageSize:vm.pageSize,pageNo:vm.currentPage}).then((newHis)=>{
                    vm.totalCount = newHis.data.data.totalCount;
                    // newHis.data.data.list.forEach(it=>{
                    //     it.changed_summary = JSON.parse(it.changed_summary);
                    //     it.changed_content = JSON.parse(it.changed_content);
                    // });
                    vm.configList=newHis.data.data.list;
                    vm.context = newHis.data.context;
                    vm.listLoading = false;
                    
                    vm.$message({
                             showClose:true,
                             type:'success',
                             message:'完成装载'
                         });
                }).catch(ex=>{
                    vm.listLoading = false;
                    vm.$message({showClose:true,type:"error",message:ex});
                });
                
            }

            function manage(row,cmd,name)
            {
               let inputPlaceholder =`对${row.changed_user}在${new Date(row.changed_time).toLocaleDateString()}的变更[${row.changed_id}]执行[${name}]操作`;
               vm.$prompt(`此次[${name}]操作确定后会立即执行, 是否继续?`, '提示', {
                          confirmButtonText: '确定',
                          cancelButtonText: '取消',
                          inputPlaceholder:inputPlaceholder,
                        }).then(({ value }) => {
                          console.warn(value);
                          doManage(value?value:inputPlaceholder);
                        }).catch(() => {
                           console.log(`${name} is cancelled`);
                        });
                
                function doManage(remark)
                {
                    axios.post(window.hosturl +`/change-manage`,{changeId:row.changed_id,remark:remark,action:cmd}).then(
                    resp =>{
                        if(typeof resp.data!=='object' || resp.data.error)
                        {
                            vm.$message({
                                    showClose:true,
                                     type:'error',
                                     message:resp.data.data
                                 });
                            return;
                        }
                       refresh();
                       if(cmd == 'stored')
                       {
                           vm.triggerGrayPublish(row);
                       }
                    }).catch(ex=>{vm.$message({showClose:true,type:"error",message:ex});});
                }
            }
        // 
        let changedStatusNames =['已完成','已终止','灰度中','待发布','已放弃','计划中'];
        let changedStatusCodes =['published', 'terminated', 'graying', 'stored', 'canceled', 'planed'];
        window.vm = new Vue({
            el: '#app',
            data: {
                listLoading:false,
                configList:[],
                pageSize:10,
                currentPage:1,
                totalCount:1,
                changedStatusNames:changedStatusNames,
                changedStatusCodes:changedStatusCodes,
                context:{},
                profile:$util.getUrlArgOrDefault('profile','default'),
            },
            methods: {
                handleChangeManage(change,action,name)
                {
                    manage(change,action,name)
                },
                triggerGrayPublish(row)
                {
                    window.vmGrayPublish.showform(row,refresh);
                },
                handleRestore:function(row,cmd,name)
                {
                    restore(row,cmd,name);
                },
                handleSizeChange(val) {
                    this.pageSize =val;
                    refresh()
                 },
              handleCurrentChange(val) {
                this.currentPage=val;
                refresh()
              },
            },
        });

        refresh();
        
        </script>

        <div id='gray-publish' style="display: none;">
            <el-dialog width="70%" :visible.sync="showGrayPublishDlg" title="应用发布列表">
                <el-row>
                    <el-col :span="8">
                        <el-button size="mini" :disabled="this.selectedCount == 0 " type="primary" @click="handleGrayPublish(false)">发布到选中节点</el-button>
                        <el-checkbox size="mini" v-model="donotClose">未完成不关闭窗口</el-checkbox>
                    </el-col>
                    <el-col :span="8">
                        &nbsp;<el-link size="mini" icon="el-icon-scissors" type="primary" plain @click="handleGrayPublish(true)">强行结束发布</el-link>
                    </el-col>
                    <el-col :span="8">
                        <el-button size="mini" type="success" @click="showGrayPublishDlg=false">关 闭 窗口 返 回</el-button>
                        <el-link size="mini" plain type="success" @click="handleGetAgain" :icon="loading?'el-icon-loading':'el-icon-refresh-left'">刷新</el-link>
                    </el-col>
                </el-row>
                <el-table v-loading.lock="loading" max-height="400px" border :data="changeSummary.nodes" style="width: 100%;margin-bottom: 20px;">
                    <el-table-column prop="name" label="应用节点" width='200px' :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <el-checkbox :disabled="scope.row.time?true:false || !scope.row.need" v-model="scope.row.selected"
                                @change="selectedCount += $event?1:-1;">{{scope.row.name}}</el-checkbox>
                        </template>
                    </el-table-column>
                    <el-table-column prop="profiles" label="应用环境" :show-overflow-tooltip="true">
                        <template slot-scope="scope">
                            <span>{{scope.row.profiles}}</span>
                        </template>
                    </el-table-column>
                    <el-table-column prop="ackTime" label="最后响应时间" width='200px'>
                        <template slot-scope="scope">
                            <span v-if="scope.row.ackTime">{{new Date(scope.row.ackTime).toLocaleDateString() + ' ' + new Date(scope.row.ackTime ).toLocaleTimeString()}}</span>
                        </template>
                    </el-table-column>
                    <el-table-column label="发布时间" prop="time" width='200px'>
                        <template slot-scope="scope">
                            <span>{{scope.row.time?new Date(scope.row.ackTime).toLocaleDateString() + ' ' + new Date(scope.row.ackTime ).toLocaleTimeString():""}}</span>
                        </template>
                    </el-table-column>
                </el-table>
            </el-dialog>
        </div>

        <script type="module" id="gray-publish-script">
            window.vmGrayPublish = new Vue({
               el: '#gray-publish',
               data: {
                   showGrayPublishDlg: false,
                   applicationNodes:{},
                   change:{},
                   changeSummary:{},
                   checkCount : 2,
                   checkingTick:2,
                   loading:true,
                   selectedCount:0,
                   callback:null,
                   donotClose:true,
               },
               mounted:function()
               {
                   this.$el.style.display= 'block';
               },
               methods: {
                   handleGrayPublish(terminated)
                   {
                       if(terminated)
                       {
                           this.$confirm(`[终止发布]会完成选中节点发布，剩余节点不再发布, 是否继续?`, '提示', {
                               cancelButtonText: '取消',
                               confirmButtonText: '确定',
                               type: 'warning',
                               center: true
                           }).then(()=>this.doGrayPublish(terminated))
                             .catch(e=>{console.log('canceled');});
                        }
                      else this.doGrayPublish(false);
                      
                    },
                     
                    doGrayPublish(terminated)
                    {
                       let lst =[];
                       let left = 0;
                       this.changeSummary.nodes.forEach(it=>{
                           if(it.time || !it.need) return;
                           if(it.selected)
                           {
                               lst.push(it.name);
                           } else left +=1;
                       });
                           
                     let status = "graying";
                     if(terminated) status = "terminated";
                     if(left == 0) status = "published";
                      
                     console.log("List to publish is ",lst);
                     
                     let self = this;
                     axios.post(window.hosturl +`/gray-publish`,{changeId:self.change.changed_id,status:status,list:lst}).then(resp=>{
                         if(typeof resp.data!=='object' || resp.data.error)
                         {
                             self.$message({showClose:true,type:"error",message:resp.data});
                             return;
                         }
                         
                         if(self.donotClose && status == 'graying')
                         {
                             let newsum = resp.data.data.changed_summary;
                             self.changeSummary.nodes.forEach(it=>{
                                 let finded = newsum.nodes.find(i=>it.name == i.name);
                                 if(finded)self.$set(it,"time",finded.time);
                             })
                         } else self.showGrayPublishDlg = false;
                         
                         if(self.callback) self.callback(status);
                         
                     }).catch(ex=>{
                         self.$message({showClose:true,type:"error",message:ex});
                     });
                   },
                   handleGetAgain(){
                       let self = this;
                       self.checkingTick = self.checkCount;
                       self.nodesGet();
                   },
                  showform(change,callback)
                  {
                      let chg = JSON.parse(JSON.stringify(change));
                      this.change = chg;
                      this.callback = callback;
                      
                      console.log(chg.changed_summary);
                      
                      if(!chg.changed_summary.nodes) chg.changed_summary.nodes = [];
                      
                      chg.changed_summary.nodes.forEach(it=>it.selected = true);
                      
                      this.changeSummary = chg.changed_summary;
                      this.selectedCount = 0;
                      this.nodesGather();
                  },
                  nodesGather()
                  {
                      let self = this;
                      axios.get(window.hosturl +`/nodes-gather`).then(resp=>{
                          if(typeof resp.data!=='object' || resp.data.error)
                          {
                              self.$message({showClose:true,type:"error",message:resp.data});
                              return;
                          }
                          self.checkingTick = self.checkCount;
                          self.nodesGet();
                          self.showGrayPublishDlg = true;
                        
                      }).catch(ex=>{
                          self.$message({showClose:true,type:"error",message:ex});
                      });
                  },
                  nodesGet()
                  {
                      let self = this;
                      self.loading = true;
                      axios.get(window.hosturl +`/nodes-get`).then(resp=>{
                          if(typeof resp.data!=='object' || resp.data.error)
                          {
                              self.loading = false;
                              self.$message({showClose:true,type:"error",message:resp.data});
                              return;
                          }
                          let nodes = resp.data.data;
                          self.checkingTick =  self.applicationNodes.list && self.applicationNodes.list.length === nodes.list.length ? self.checkingTick -1: self.checkCount;
                          self.applicationNodes = nodes;
                          
                          if(self.checkingTick > 0)
                          {
                              setTimeout(self.nodesGet,100);
                          }else{
                              self.mergeNodes();
                              self.loading = false;
                          };
                          
                      }).catch(ex=>{
                          self.loading = false;
                          self.$message({showClose:true,type:"error",message:ex});
                      });
                  },
                  
                  mergeNodes()
                  {
                      let self = this;
                      console.log('self.applicationNodes',self.applicationNodes);
                      console.log('self.changeSummary',self.changeSummary);
                      let profiles = self.change.changed_summary.profiles;

                      self.applicationNodes.list.forEach(node=>{
                          let existed = self.changeSummary.nodes.find(it=>it.name === node.name);
                          let need = profiles.filter(it=>node.profiles.includes(it));
                          need = need.length>0 ? true:false;
                          if(existed)
                           {
                               self.$set(existed,"ackTime",node.ackTime);
                               self.$set(existed,"profiles",node.profiles);
                               self.$set(existed,"need",need);
                               return;
                            }
                          
                          let lst =self.changeSummary.nodes;
                          lst.splice(0,0,{name:node.name,profiles:node.profiles,ackTime:node.ackTime,selected:false,need:need})
                          
                          
                      });
                    self.changeSummary.nodes.sort((a,b)=>a.name.localeCompare(b.name));
                      
                  }
               },//end of method
           });
        </script>
    </body>
</html>
